#include <video/VGA.h>
#include <asmutils.h>
#include <string.h>

/* These define our textpointer, our background and foreground
*  colors (attributes), and x and y cursor coordinates */
_u16 *vgaptr;
_s32 attrib = 0x0F;
_s32 csr_x = 0, csr_y = 0;

/* Sets our text-mode VGA pointer and clears the screen */
void init_video(void)
{
  vgaptr = (_u16 *)VGA_MEM_ADDR;
  cls();
}

void move_csr(void)
{
  _u32 index;

  /* The equation for finding the index in a linear
   *  chunk of memory can be represented by:
   *  Index = [(y * width) + x] */

  index = COORD_TO_OFFSET(csr_x, csr_y);

  /* This sends a command to indicies 14 and 15 in the
   *  CRT Control Register of the VGA controller. These
   *  are the high and low bytes of the index that show
   *  where the hardware cursor is to be 'blinking'. To
   *  learn more, you should look up some VGA specific
   *  programming documents. A great start to graphics:
   *  http://www.brackeen.com/home/vga */

  outb(VGA_CONTROL_PORT, 14);
  outb(VGA_DATA_PORT, index >> 8);
  outb(VGA_CONTROL_PORT, 15);
  outb(VGA_DATA_PORT, index);
}

/* Clears the screen */
void cls()
{
  _u32 blank;
  _s32 i;

  /* Again, we need the 'short' that will be used to
   *  represent a space with color */
  blank = 0x20 | (attrib << 8);

  /* Sets the entire screen to spaces in our current
   *  color */
  for(i = 0; i < VGA_SCREEN_ROWS; i++)
  memsetw(vgaptr + i * VGA_SCREEN_COLS, blank, VGA_SCREEN_COLS);

  /* Update out virtual cursor, and then move the
   *  hardware cursor */
  csr_x = 0;
  csr_y = 0;
  move_csr();
}

/* Scrolls the screen */
void scroll(void)
{
  _u32 blank, temp;

  blank = 0x20 | (attrib << 8);

  if(csr_y >= VGA_SCREEN_ROWS)
  {
    temp = csr_y - VGA_SCREEN_ROWS + 1;
    memcpy(vgaptr, vgaptr + temp * VGA_SCREEN_COLS, (VGA_SCREEN_ROWS - temp) * VGA_SCREEN_COLS * 2);

    memsetw (vgaptr + (VGA_SCREEN_ROWS - temp) * VGA_SCREEN_COLS, blank, VGA_SCREEN_COLS);
    csr_y = VGA_SCREEN_ROWS - 1;
  }
}

/* Puts a single character on the screen */
void putc(_u8 c)
{
  _u16 *where;
  _u32 att = attrib << 8;

  /* Backspace */
  if(c == 0x08)
  {
    if(csr_x != 0) csr_x--;
  }

  /* Tab */
  else if(c == 0x09)
  {
    csr_x = (csr_x + 8) & ~(8 - 1);
  }

  /* Cariage return */
  else if(c == '\r')
  {
    csr_x = 0;
  }

  /* Newline */
  else if(c == '\n')
  {
    csr_x = 0;
    csr_y++;
  }

   /*  Printable character */
  else if(c >= ' ')
  {
    where = vgaptr + COORD_TO_OFFSET(csr_x, csr_y);
    *where = c | att;	/* Character AND attributes: color */
    csr_x++;
  }

  /* If the cursor has reached the edge of the screen's width, insert a new line */
  if(csr_x >= VGA_SCREEN_COLS)
  {
    csr_x = 0;
    csr_y++;
  }

  /* Scroll the screen if needed, and finally move the cursor */
  scroll();
  move_csr();
}
